/*
 * Copyright 2021 liqiang
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.jansen.download.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.StringWriter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * modified by liqiang  2021-08-20
 */
public class JsonUtil {

    private static ObjectMapper om = newObjectMapper();

    private static final ObjectMapper getDefaultObjectMapper() {
        return om;
    }

    public static final ObjectMapper newObjectMapper() {
        ObjectMapper om = new ObjectMapper();
        om.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
        om.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        om.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        om.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
        om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, false);
        om.configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false);
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        om.setDefaultLeniency(true);
        return om;
    }

    public static final String toJson(Object jsonObject) {
        return toJson(null, jsonObject);
    }

    public static final String toJson(ObjectMapper objectMapper, Object jsonObject) {
        if (jsonObject != null) {
            ObjectMapper om = objectMapper != null ? objectMapper : getDefaultObjectMapper();
            StringWriter sw = new StringWriter();
            try {
                om.writeValue(sw, jsonObject);
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return sw.toString();
        }
        return "";
    }

    public static final Map<String, String> toMap(Object jsonObject) {
        return toMap(null, jsonObject);
    }

    public static final Map<String, String> toMap(ObjectMapper objectMapper, Object jsonObject) {
        String jsonString = toJson(objectMapper, jsonObject);
        return fromJsonToMap(jsonString, String.class, String.class);
    }

    public static Map<String, Object> toMapFuzzy(Object o) {
        return toMapFuzzy(null, o);
    }

    public static Map<String, Object> toMapFuzzy(ObjectMapper objectMapper, Object o) {
        return JsonUtil.fromJsonToMap(objectMapper, JsonUtil.toJson(objectMapper, o), String.class, Object.class);
    }

    public static final <T> T fromMap(Map<?, ?> jsonMap, Class<T> cls) {
        return fromMap(null, jsonMap, cls);
    }

    public static final <T> T fromMap(ObjectMapper objectMapper, Map<?, ?> jsonMap, Class<T> cls) {
        String jsonString = toJson(objectMapper, jsonMap);
        return fromJson(objectMapper, jsonString, cls);
    }

    public static final <T> List<T> fromJsonToList(String jsonString, Class<? extends T> classForEach) {
        return fromJsonToList(null, jsonString, classForEach);
    }

    public static final <T> List<T> fromJsonToList(ObjectMapper objectMapper, String jsonString, Class<? extends T> classForEach) {
        return fromJson(objectMapper, jsonString, ArrayList.class, classForEach);
    }

    public static final <T> LinkedHashSet<T> fromJsonToLinkedHashSet(String jsonString, Class<? extends T> classForEach) {
        return fromJsonToLinkedHashSet(null, jsonString, classForEach);
    }

    public static final <T> LinkedHashSet<T> fromJsonToLinkedHashSet(ObjectMapper objectMapper, String jsonString, Class<? extends T> classForEach) {
        return fromJson(objectMapper, jsonString, LinkedHashSet.class, classForEach);
    }

    public static final <T> LinkedList<T> fromJsonToLinkedList(String jsonString, Class<? extends T> classForEach) {
        return fromJsonToLinkedList(null, jsonString, classForEach);
    }

    public static final <T> LinkedList<T> fromJsonToLinkedList(ObjectMapper objectMapper, String jsonString, Class<? extends T> classForEach) {
        return fromJson(objectMapper, jsonString, LinkedList.class, classForEach);
    }

    public static final <T> Set<T> fromJsonToSet(String jsonString, Class<? extends T> classForEach) {
        return fromJsonToSet(null, jsonString, classForEach);
    }

    public static final <T> Set<T> fromJsonToSet(ObjectMapper objectMapper, String jsonString, Class<? extends T> classForEach) {
        return fromJson(objectMapper, jsonString, HashSet.class, classForEach);
    }

    public static final <T> LinkedHashSet<T> fromJsonToLinkedSet(String jsonString, Class<? extends T> classForEach) {
        return fromJsonToLinkedSet(null, jsonString, classForEach);
    }

    public static final <T> LinkedHashSet<T> fromJsonToLinkedSet(ObjectMapper objectMapper, String jsonString, Class<? extends T> classForEach) {
        return fromJson(objectMapper, jsonString, LinkedHashSet.class, classForEach);
    }

    public static final <K, V> Map<K, V> fromJsonToMap(String jsonString, Class<K> classForKey, Class<V> classForValue) {
        return fromJsonToMap(null, jsonString, classForKey, classForValue);
    }

    public static final <K, V> Map<K, V> fromJsonToMap(ObjectMapper objectMapper, String jsonString, Class<K> classForKey, Class<V> classForValue) {
        return fromJson(objectMapper, jsonString, HashMap.class, classForKey, classForValue);
    }

    public static final <K, V> LinkedHashMap<K, V> fromJsonToLinkedHashMap(String jsonString, Class<K> classForKey, Class<V> classForValue) {
        return fromJsonToLinkedHashMap(null, jsonString, classForKey, classForValue);
    }

    public static final <K, V> LinkedHashMap<K, V> fromJsonToLinkedHashMap(ObjectMapper objectMapper, String jsonString, Class<K> classForKey, Class<V> classForValue) {
        return fromJson(objectMapper, jsonString, LinkedHashMap.class, classForKey, classForValue);
    }


    public static final <T> T fromJson(String jsonString, TypeRef<? extends T> typeRef) {
        return fromJson(null, jsonString, typeRef);
    }

    public static final <T> T fromJson(ObjectMapper objectMapper, String jsonString, TypeRef<? extends T> typeRef) {
        if (jsonString == null || typeRef == null) {
            return null;
        }

        try {
            ObjectMapper om = objectMapper != null ? objectMapper : getDefaultObjectMapper();
            return om.readValue(jsonString, typeRef);
        } catch (Throwable e) {
        }
        return null;
    }

    public static final <T> T fromJson(String jsonString, Type type) {
        return fromJson(null, jsonString, type);
    }

    public static final <T> T fromJson(ObjectMapper objectMapper, String jsonString, Type type) {

        if (jsonString == null || jsonString.trim().length() == 0) {
            return null;
        }

        try {
            ObjectMapper om = objectMapper != null ? objectMapper : getDefaultObjectMapper();
            return om.readValue(jsonString, new TypeReference<T>() {
                @Override
                public Type getType() {
                    return type;
                }
            });
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static final <T> T fromJson(String jsonString, Class<T> collectionClass, Class<?>... entityClass) {
        return fromJson(null, jsonString, collectionClass, entityClass);
    }

    public static final <T> T fromJson(ObjectMapper objectMapper, String jsonString, Class<T> collectionClass, Class<?>... entityClass) {

        if (jsonString == null || collectionClass == null) {
            return null;
        }

        if (entityClass == null || entityClass.length == 0) {
            try {
                ObjectMapper om = objectMapper != null ? objectMapper : getDefaultObjectMapper();
                return om.readValue(jsonString, collectionClass);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        } else {
            try {
                ObjectMapper om = getDefaultObjectMapper();
                JavaType type = om.getTypeFactory().constructParametricType(collectionClass, entityClass);
                return om.readValue(jsonString, type);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    public static final <T> T copyBean(T bean) {
        return copyBean(null, bean);
    }

    public static final <T> T copyBean(ObjectMapper objectMapper, T bean) {
        return bean == null ? null : (T) copyBean(objectMapper, bean, bean.getClass());
    }

    public static final <T> T copyBean(Object bean, Class<T> cls) {
        return copyBean(null, bean, cls);
    }

    public static final <T> T copyBean(Object bean, TypeRef<T> cls) {
        return copyBean(null, bean, cls);
    }

    public static final <T> T copyBean(ObjectMapper objectMapper, Object bean, Class<T> cls) {
        String jsonString = toJson(objectMapper, bean);
        return fromJson(objectMapper, jsonString, cls);
    }

    public static final <T> T copyBean(ObjectMapper objectMapper, Object bean, TypeRef<T> cls) {
        String jsonString = toJson(objectMapper, bean);
        return fromJson(objectMapper, jsonString, cls);
    }

    public static final <T> List<T> copyList(Object bean, Class<T> cls) {
        return copyList(null, bean, cls);
    }

    public static final <T> List<T> copyList(ObjectMapper objectMapper, Object bean, Class<T> cls) {
        String jsonString = toJson(objectMapper, bean);
        return fromJsonToList(objectMapper, jsonString, cls);
    }


}
